package fr.sedoo.commons.client.widget.map.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.gwtopenmaps.openlayers.client.Bounds;
import org.gwtopenmaps.openlayers.client.LonLat;
import org.gwtopenmaps.openlayers.client.Map;
import org.gwtopenmaps.openlayers.client.MapOptions;
import org.gwtopenmaps.openlayers.client.MapWidget;
import org.gwtopenmaps.openlayers.client.Projection;
import org.gwtopenmaps.openlayers.client.control.DragPan;
import org.gwtopenmaps.openlayers.client.control.DrawFeature;
import org.gwtopenmaps.openlayers.client.control.DrawFeature.FeatureAddedListener;
import org.gwtopenmaps.openlayers.client.control.DrawFeatureOptions;
import org.gwtopenmaps.openlayers.client.control.MousePosition;
import org.gwtopenmaps.openlayers.client.control.MousePositionOptions;
import org.gwtopenmaps.openlayers.client.control.MousePositionOutput;
import org.gwtopenmaps.openlayers.client.control.Navigation;
import org.gwtopenmaps.openlayers.client.control.OverviewMap;
import org.gwtopenmaps.openlayers.client.control.ScaleLine;
import org.gwtopenmaps.openlayers.client.control.SelectFeature;
import org.gwtopenmaps.openlayers.client.control.SelectFeature.ClickFeatureListener;
import org.gwtopenmaps.openlayers.client.control.SelectFeatureOptions;
import org.gwtopenmaps.openlayers.client.feature.VectorFeature;
import org.gwtopenmaps.openlayers.client.handler.RegularPolygonHandler;
import org.gwtopenmaps.openlayers.client.handler.RegularPolygonHandlerOptions;
import org.gwtopenmaps.openlayers.client.layer.GoogleV3;
import org.gwtopenmaps.openlayers.client.layer.GoogleV3MapType;
import org.gwtopenmaps.openlayers.client.layer.GoogleV3Options;
import org.gwtopenmaps.openlayers.client.layer.OSM;
import org.gwtopenmaps.openlayers.client.layer.Vector;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.ToggleButton;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

import fr.sedoo.commons.client.image.CommonBundle;
import fr.sedoo.commons.client.image.GeographicalBundle;
import fr.sedoo.commons.client.message.GeographicalMessages;
import fr.sedoo.commons.client.util.ElementUtil;
import fr.sedoo.commons.client.util.UuidUtil;
import fr.sedoo.commons.shared.domain.GeographicBoundingBoxDTO;

public class AreaSelectorWidget extends Composite implements ClickHandler, RequiresResize {

	private static final int TOOLBAR_DEFAULT_WIDTH = 97;

	private static MapWidgetUiBinder uiBinder = GWT.create(MapWidgetUiBinder.class);

	protected static final String GPS_PROJECTION_NAME = "EPSG:4326";
	protected static final Projection DEFAULT_PROJECTION = new Projection(GPS_PROJECTION_NAME);
	protected static final int TOOLBAR_VERTICAL_OFFSET = 5;
	protected static final int TOOLBAR_HORIZONTAL_OFFSET = 50;

	protected Map map;

	private boolean autoswitchToDragControl = false;

	private final List<RectangularAreaListener> listeners = new ArrayList<RectangularAreaListener>();

	@UiField
	VerticalPanel contentPanel;
	// AbsolutePanel contentPanel;

	private ToggleButton drawRectangularAreaButton;
	private ToggleButton eraseRectangularAreaButton;
	private ToggleButton dragPanButton;

	protected DrawFeature drawRectangularAreaControl = null;
	private SelectFeature hoverRectangularAreaSelectFeature = null;

	private final NumberFormat doubleFormatter = NumberFormat.getFormat("#.0000");

	HashMap<String, VectorFeature> rectangularFeatures = new HashMap<String, VectorFeature>();

	Vector rectangularAreaLayer;

	private HorizontalPanel toolBar;

	public static final int MIN_WIDTH = 256;
	public static final int MED_WIDTH = 512;
	public static final int MAX_WIDTH = 1024;
	public static final int MIN_HEIGHT = 200;
	public static final int MED_HEIGHT = 400;
	public static final int MAX_HEIGHT = 800;

	private static final int DEFAULT_WIDTH = MED_WIDTH;
	private static final int DEFAULT_HEIGHT = MED_HEIGHT;

	private int width = DEFAULT_WIDTH;
	private int height = DEFAULT_HEIGHT;

	private List<ToggleButton> buttonList;

	private MapWidget mapWidget;

	public static final String OSM_LAYER = "OSM";

	public static final String GOOGLE_LAYER = "GOOGLE";

	public static final String DEFAULT_MAP_LAYER = GOOGLE_LAYER;

	private String mapLayer;

	private int mapWidth = MED_WIDTH;

	private UIObject sizeContainer;

	interface MapWidgetUiBinder extends UiBinder<Widget, AreaSelectorWidget> {
	}

	public AreaSelectorWidget() {
		this(DEFAULT_WIDTH, DEFAULT_HEIGHT);
	}

	public AreaSelectorWidget(String mapLayer) {
		this(DEFAULT_WIDTH, DEFAULT_HEIGHT, mapLayer);
	}

	public AreaSelectorWidget(int width, int height) {
		this(width, height, DEFAULT_MAP_LAYER);
	}

	public AreaSelectorWidget(int width, int height, String mapLayer) {
		this.mapLayer = mapLayer;
		if ((mapLayer.compareToIgnoreCase(GOOGLE_LAYER) != 0) || (mapLayer.compareToIgnoreCase(OSM_LAYER) != 0)) {
			mapLayer = DEFAULT_MAP_LAYER;
		}
		CommonBundle.INSTANCE.css().ensureInjected();
		initWidget(uiBinder.createAndBindUi(this));
		this.width = width;
		this.height = height;
		init();
		onResize();
	}

	protected void init() {

		MapOptions defaultMapOptions = new MapOptions();
		defaultMapOptions.setDisplayProjection(DEFAULT_PROJECTION);
		defaultMapOptions.setNumZoomLevels(20);

		mapWidget = new MapWidget(width + "px", height + "px", defaultMapOptions);
		map = mapWidget.getMap();

		if (mapLayer.compareToIgnoreCase(GOOGLE_LAYER) == 0) {
			addGoogleLayers(map);
		} else {
			addOSMLayers(map);
		}
		map.addControl(new OverviewMap());
		map.addControl(new ScaleLine());
		map.addControl(new Navigation());
		map.addControl(new DragPan());

		MousePositionOutput mpOut = new MousePositionOutput() {
			@Override
			public String format(LonLat lonLat, Map map) {

				String out = "";
				if (mapLayer.compareTo(GOOGLE_LAYER) == 0) {
					out += "<div style=\"color:white;background-color:black;\"><b>Longitude : </b> ";
					out += doubleFormatter.format(lonLat.lon());
					out += "<b><br>Latitude : </b> ";
					out += doubleFormatter.format(lonLat.lat());
					out += "</div>";
				} else {
					out += "<span style=\"color:white;background-color:black;\"><b>Longitude : </b> ";
					out += doubleFormatter.format(lonLat.lon());
					out += "<b>, Latitude : </b> ";
					out += doubleFormatter.format(lonLat.lat());
					out += "</span>";
				}
				return out;
			}
		};

		MousePositionOptions mpOptions = new MousePositionOptions();
		mpOptions.setFormatOutput(mpOut);

		map.addControl(new MousePosition(mpOptions));

		rectangularAreaLayer = new Vector("RectangularAreaLayer");
		map.addLayer(rectangularAreaLayer);

		RectangularAreaLayerListener rectangularAreaLayerListener = new RectangularAreaLayerListener();

		// Contrôle gérant le dessin du rectangle
		DrawFeatureOptions rectangularAreaOptions = new DrawFeatureOptions();
		rectangularAreaOptions.onFeatureAdded(rectangularAreaLayerListener);
		RegularPolygonHandlerOptions regularPolygonHandlerOptions = new RegularPolygonHandlerOptions();
		regularPolygonHandlerOptions.setSides(4);
		regularPolygonHandlerOptions.setIrregular(true);
		rectangularAreaOptions.setHandlerOptions(regularPolygonHandlerOptions);
		drawRectangularAreaControl = new DrawFeature(rectangularAreaLayer, new RegularPolygonHandler(), rectangularAreaOptions);

		// Contrôle gérant la selection du rectangle
		SelectFeatureOptions rectangularAreaHoverSelectFeatureOptions = new SelectFeatureOptions();
		rectangularAreaHoverSelectFeatureOptions.setHover();
		rectangularAreaHoverSelectFeatureOptions.clickFeature(rectangularAreaLayerListener);
		hoverRectangularAreaSelectFeature = new SelectFeature(rectangularAreaLayer, rectangularAreaHoverSelectFeatureOptions);

		drawRectangularAreaControl.deactivate();
		hoverRectangularAreaSelectFeature.deactivate();

		map.addControl(drawRectangularAreaControl);
		map.addControl(hoverRectangularAreaSelectFeature);

		// force the map to fall behind popups
		mapWidget.getElement().getFirstChildElement().getStyle().setZIndex(0);

		mapWidget.addDomHandler(new MapMouseOverHandler(), MouseOverEvent.getType());
		toolBar = new HorizontalPanel();
		toolBar.setStylePrimaryName("map-selector-toolbar");
		toolBar.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);
		toolBar.setSpacing(2);
		drawRectangularAreaButton = new ToggleButton(new Image(GeographicalBundle.INSTANCE.drawRectangularArea()));
		drawRectangularAreaButton.setTitle(GeographicalMessages.INSTANCE.drawRectangularAreaTooltip());
		eraseRectangularAreaButton = new ToggleButton(new Image(GeographicalBundle.INSTANCE.eraseRectangularArea()));
		eraseRectangularAreaButton.setTitle(GeographicalMessages.INSTANCE.eraseRectangularAreaTooltip());
		dragPanButton = new ToggleButton(new Image(GeographicalBundle.INSTANCE.drag()));
		dragPanButton.setTitle(GeographicalMessages.INSTANCE.dragPanButtonTooltip());
		initToolbar();
		dragPanButton.setDown(true);
		contentPanel.add(toolBar);
		contentPanel.add(mapWidget);
		contentPanel.setStylePrimaryName("map-selector-panel");
		LonLat rightLowerDisplay = new LonLat(180, -89);
		LonLat leftUpperDisplay = new LonLat(-180, 89);
		rightLowerDisplay.transform(DEFAULT_PROJECTION.getProjectionCode(), map.getProjection());
		leftUpperDisplay.transform(DEFAULT_PROJECTION.getProjectionCode(), map.getProjection());
		Bounds wholeWorldBound = new Bounds(leftUpperDisplay.lon(), rightLowerDisplay.lat(), rightLowerDisplay.lon(), leftUpperDisplay.lat());
		map.setMaxExtent(wholeWorldBound);
		center();
		onResize();
	}

	public void setDrawRectangularAreaTooltip(String text) {
		drawRectangularAreaButton.setTitle(text);
	}

	public void setEraseRectangularAreaTooltip(String text) {
		eraseRectangularAreaButton.setTitle(text);
	}

	private void addOSMLayers(Map map) {
		OSM osm_1 = OSM.Mapnik("Mapnik");
		osm_1.setIsBaseLayer(true);
		map.addLayer(osm_1);
	}

	protected List<ToggleButton> getToolBarButtons() {
		if (buttonList == null) {
			buttonList = new ArrayList<ToggleButton>();
			buttonList.add(drawRectangularAreaButton);
			buttonList.add(eraseRectangularAreaButton);
			buttonList.add(dragPanButton);
		}
		return buttonList;
	}

	private void addGoogleLayers(Map map) {

		GoogleV3Options gHybridOptions = new GoogleV3Options();
		gHybridOptions.setIsBaseLayer(true);
		gHybridOptions.setType(GoogleV3MapType.G_HYBRID_MAP);
		GoogleV3 gHybrid = new GoogleV3("Google Hybrid", gHybridOptions);

		GoogleV3Options gNormalOptions = new GoogleV3Options();
		gNormalOptions.setIsBaseLayer(true);
		gNormalOptions.setType(GoogleV3MapType.G_NORMAL_MAP);
		GoogleV3 gNormal = new GoogleV3("Google Normal", gNormalOptions);

		GoogleV3Options gSatelliteOptions = new GoogleV3Options();
		gSatelliteOptions.setIsBaseLayer(true);
		gSatelliteOptions.setType(GoogleV3MapType.G_SATELLITE_MAP);
		GoogleV3 gSatellite = new GoogleV3("Google Satellite", gSatelliteOptions);

		GoogleV3Options gTerrainOptions = new GoogleV3Options();
		gTerrainOptions.setIsBaseLayer(true);
		gTerrainOptions.setType(GoogleV3MapType.G_TERRAIN_MAP);
		GoogleV3 gTerrain = new GoogleV3("Google Terrain", gTerrainOptions);

		map.addLayer(gHybrid);
		map.addLayer(gNormal);
		map.addLayer(gSatellite);
		map.addLayer(gTerrain);

	}

	protected void initToolbar() {
		Iterator<ToggleButton> iterator = getToolBarButtons().iterator();
		while (iterator.hasNext()) {
			ToggleButton toggleButton = iterator.next();
			toggleButton.addClickHandler(this);
			toolBar.add(toggleButton);

		}
	}

	/**
	 * This handler force the updateSize commande whenever the mouse enters the
	 * map. It avoids the offset between the mouse pointer and drawn feature in
	 * case of an inner scrolled div
	 * 
	 * 
	 */
	class MapMouseOverHandler implements MouseOverHandler {

		@Override
		public void onMouseOver(MouseOverEvent event) {
			map.updateSize();
		}

	}

	class RectangularAreaLayerListener implements ClickFeatureListener, FeatureAddedListener {

		@Override
		public void onFeatureClicked(VectorFeature vectorFeature) {
			String uuid = vectorFeature.getFeatureId();
			if (uuid != null) {
				destroy(uuid);
			}
		}

		@Override
		public void onFeatureAdded(VectorFeature vectorFeature) {
			Bounds bounds = vectorFeature.getGeometry().getBounds();
			LonLat lowerLeft = new LonLat(bounds.getLowerLeftX(), bounds.getLowerLeftY());
			LonLat upperRight = new LonLat(bounds.getUpperRightX(), bounds.getUpperRightY());
			lowerLeft.transform(map.getProjection(), DEFAULT_PROJECTION.getProjectionCode());
			upperRight.transform(map.getProjection(), DEFAULT_PROJECTION.getProjectionCode());

			if (isAutoswitchToDragControl()) {
				drawRectangularAreaControl.deactivate();
				dragPanButton.setDown(true);
				drawRectangularAreaButton.setDown(false);
			}

			GeographicBoundingBoxDTO box = new GeographicBoundingBoxDTO();
			box.setEastBoundLongitude(doubleFormatter.format(upperRight.lon()));
			box.setWestBoundLongitude(doubleFormatter.format(lowerLeft.lon()));
			box.setSouthBoundLatitude(doubleFormatter.format(lowerLeft.lat()));
			box.setNorthBoundLatitude(doubleFormatter.format(upperRight.lat()));
			String uuid = UuidUtil.uuid();
			box.setIdentifier(uuid);
			vectorFeature.setFeatureId(uuid);
			rectangularFeatures.put(uuid, vectorFeature);
			// We need to redraw the layer for the hover mechanism to be active
			rectangularAreaLayer.redraw();

			Iterator<RectangularAreaListener> iterator = listeners.iterator();
			while (iterator.hasNext()) {

				iterator.next().onRectangleAdded(box);
			}
		}
	}

	public GeographicBoundingBoxDTO getGeographicBoundingBoxDTO() {
		GeographicBoundingBoxDTO result = new GeographicBoundingBoxDTO();
		return result;
	}

	public void setGeographicBoundingBoxDTO(GeographicBoundingBoxDTO box) {
		reset();
		center(box, true);
	}

	public void reset() {

		// Suppression des rectangles existants
		rectangularAreaLayer.destroyFeatures();
		deactivateAllControls();
		rectangularFeatures.clear();
		center();
	}

	public void graphicalUpdate() {
		if (mapLayer.compareToIgnoreCase(GOOGLE_LAYER) == 0) {

			Timer timer = new Timer() {
				@Override
				public void run() {
					map.updateSize();
					map.pan(1, 1);
					map.pan(-1, -1);
				}
			};

			timer.schedule(1000);
		}
	}

	public void center() {
		map.zoomToMaxExtent();
		graphicalUpdate();
	}

	public void center(GeographicBoundingBoxDTO box, boolean displayDefault) {

		LonLat rightLowerDisplay;
		LonLat leftUpperDisplay;

		if (box.validate().isEmpty()) {
			LonLat rightLower = box.getRightLowerCorner();
			LonLat leftUpper = box.getLeftUpperCorner();

			rightLowerDisplay = box.getRightLowerDisplayCorner();
			leftUpperDisplay = box.getLeftUpperDisplayCorner();

			rightLower.transform(DEFAULT_PROJECTION.getProjectionCode(), map.getProjection());
			leftUpper.transform(DEFAULT_PROJECTION.getProjectionCode(), map.getProjection());

			Bounds bounds = new Bounds();
			bounds.extend(rightLower);
			bounds.extend(leftUpper);
			// reactangularAreaFeature = new VectorFeature(bounds.toGeometry());
			// rectangularAreaLayer.addFeature(reactangularAreaFeature);
			Bounds displayBounds = new Bounds(leftUpperDisplay.lon(), rightLowerDisplay.lat(), rightLowerDisplay.lon(), leftUpperDisplay.lat());
			map.zoomToExtent(displayBounds, true);
			/* we simulate a pan to correct some google strange behaviour */
			map.pan(1, 1);
			map.pan(-1, -1);
		} else {
			// On affiche une carte complÃ¨te du monde par dÃ©faut
			map.zoomToMaxExtent();
		}
	}

	public void activateDrawRectangularAreaControl() {
		Iterator<ToggleButton> iterator = getToolBarButtons().iterator();
		while (iterator.hasNext()) {
			ToggleButton toggleButton = iterator.next();
			toggleButton.setDown(false);
		}
		deactivateAllControls();
		drawRectangularAreaButton.setDown(true);
		drawRectangularAreaControl.activate();
	}

	@Override
	public void onClick(ClickEvent event) {
		if (event.getSource() instanceof ToggleButton) {
			Iterator<ToggleButton> iterator = getToolBarButtons().iterator();
			while (iterator.hasNext()) {
				ToggleButton toggleButton = iterator.next();
				toggleButton.setDown(false);
			}
			((ToggleButton) event.getSource()).setDown(true);

			deactivateAllControls();

			if (event.getSource() == drawRectangularAreaButton) {
				drawRectangularAreaControl.activate();
			}

			if (event.getSource() == eraseRectangularAreaButton) {
				hoverRectangularAreaSelectFeature.activate();
			}

		}
	}

	protected void deactivateAllControls() {
		drawRectangularAreaControl.deactivate();
		hoverRectangularAreaSelectFeature.deactivate();
	}

	public void enableDisplayMode() {
		changeButtonVisibility(false);
	}

	public void enableEditMode() {
		changeButtonVisibility(true);
		activateDrawRectangularAreaControl();
	}

	protected void changeButtonVisibility(boolean visible) {
		if (visible) {
			ElementUtil.show(toolBar);
		} else {
			ElementUtil.hide(toolBar);
		}
		// Iterator<ToggleButton> iterator = getToolBarButtons().iterator();
		// while (iterator.hasNext()) {
		// ToggleButton toggleButton = iterator.next();
		// toggleButton.setVisible(visible);
		// }
	}

	public boolean isAutoswitchToDragControl() {
		return autoswitchToDragControl;
	}

	public void setAutoswitchToDragControl(boolean autoswitchToDragControl) {
		this.autoswitchToDragControl = autoswitchToDragControl;
	}

	public void addListener(RectangularAreaListener listener) {
		listeners.add(listener);
	}

	public Map getMap() {
		return map;
	}

	public void pushMapToBack() {
		if (mapWidget != null) {
			mapWidget.getElement().getFirstChildElement().getStyle().setZIndex(0);
		}
	}

	public MapWidget getMapWidget() {
		return mapWidget;
	}

	@Override
	public void onResize() {
		map.updateSize();
		int aux = toolBar.getOffsetWidth();
		if (aux == 0) {
			aux = TOOLBAR_DEFAULT_WIDTH;
		}
		// contentPanel.setWidgetPosition(toolBar,
		// (contentPanel.getOffsetWidth()) / 2 + mapWidth / 2 - aux -
		// TOOLBAR_HORIZONTAL_OFFSET, TOOLBAR_VERTICAL_OFFSET);
		// contentPanel.setWidgetPosition(toolBar, TOOLBAR_HORIZONTAL_OFFSET,
		// TOOLBAR_VERTICAL_OFFSET);
	}

	public void setSizeContainer(UIObject container) {
		this.sizeContainer = container;
		onResize();
	}

	public void destroyAllBut(String uuid) {
		Set<String> keySet = new TreeSet<String>();
		keySet.addAll(rectangularFeatures.keySet());
		Iterator<String> iterator = keySet.iterator();
		while (iterator.hasNext()) {
			String current = iterator.next();
			if (current.compareTo(uuid) != 0) {
				destroy(current);
			}
		}
	}

	public void destroy(String uuid) {
		deleteFeatureFromUuid(uuid);

		Iterator<RectangularAreaListener> iterator = listeners.iterator();
		while (iterator.hasNext()) {
			iterator.next().onRectangleDeleted(uuid);
		}
	}

	public void add(GeographicBoundingBoxDTO box) {

		if (box.isValid()) {
			// We add the corresponding feature
			addFeatureFromBox(box);

			// We warn listeners
			Iterator<RectangularAreaListener> iterator = listeners.iterator();
			while (iterator.hasNext()) {

				iterator.next().onRectangleAdded(box);
			}
			graphicalUpdate();
		}
	}

	private void deleteFeatureFromUuid(String uuid) {
		VectorFeature vectorFeature = rectangularFeatures.get(uuid);
		if (vectorFeature != null) {
			rectangularFeatures.remove(uuid);
			vectorFeature.destroy();
		}
	}

	private void addFeatureFromBox(GeographicBoundingBoxDTO box) {
		LonLat rightLower = box.getRightLowerCorner();
		LonLat leftUpper = box.getLeftUpperCorner();

		rightLower.transform(DEFAULT_PROJECTION.getProjectionCode(), map.getProjection());
		leftUpper.transform(DEFAULT_PROJECTION.getProjectionCode(), map.getProjection());

		Bounds bounds = new Bounds();
		bounds.extend(rightLower);
		bounds.extend(leftUpper);
		VectorFeature vectorFeature = new VectorFeature(bounds.toGeometry());
		vectorFeature.setFeatureId(box.getIdentifier());
		rectangularAreaLayer.addFeature(vectorFeature);
		rectangularFeatures.put(box.getIdentifier(), vectorFeature);
		// We need to redraw the layer for the hover mechanism to be active
		rectangularAreaLayer.redraw();
	}

	public void destroyAll() {
		Set<String> keySet = new TreeSet<String>();
		keySet.addAll(rectangularFeatures.keySet());
		Iterator<String> iterator = keySet.iterator();
		while (iterator.hasNext()) {
			destroy(iterator.next());
		}

	}

	public void update(GeographicBoundingBoxDTO box) {
		VectorFeature vectorFeature = rectangularFeatures.get(box.getIdentifier());
		if (box.isValid()) {

			// The feature is valid, it should either update an existing one or
			// be added
			if (vectorFeature == null) {
				addFeatureFromBox(box);
			} else {
				deleteFeatureFromUuid(box.getIdentifier());
				addFeatureFromBox(box);
			}
		} else {
			// Box isn't valid, we should just delete corresponding feature if
			// any
			if (vectorFeature != null) {
				deleteFeatureFromUuid(box.getIdentifier());
				addFeatureFromBox(box);
			}
		}

		// We warn listeners
		Iterator<RectangularAreaListener> iterator = listeners.iterator();
		while (iterator.hasNext()) {

			iterator.next().onRectangleUpdated(box);
		}
	}

	public void zoomTo(String identifier) {
		VectorFeature vectorFeature = rectangularFeatures.get(identifier);
		if (vectorFeature != null) {
			map.zoomToExtent(vectorFeature.getGeometry().getBounds(), false);
		}
	}

	public void zoomAll() {
		if (rectangularFeatures.isEmpty() == false) {
			Iterator<VectorFeature> iterator = rectangularFeatures.values().iterator();
			Bounds bounds = new Bounds();
			while (iterator.hasNext()) {
				VectorFeature current = iterator.next();
				bounds.extend(current.getGeometry().getBounds());
			}
			map.zoomToExtent(bounds, false);
		}
	}

	public void hover(String identifier) {
		VectorFeature vectorFeature = rectangularFeatures.get(identifier);
		if (vectorFeature != null) {
			hoverRectangularAreaSelectFeature.select(vectorFeature);
		}
	}

	public void unhover(String identifier) {
		VectorFeature vectorFeature = rectangularFeatures.get(identifier);
		if (vectorFeature != null) {
			hoverRectangularAreaSelectFeature.unSelect(vectorFeature);
		}
	}

}
